#ifndef __MY_LOGGER__
#define __MY_LOGGER__

#ifndef GUN_SOURCE
#define GUN_SOURCE
#endif

#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "message.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>

namespace mylog
{
    class Logger
    {
    protected:
        std::string _logger_name;
        Formater::ptr _formater;
        std::vector<LogSink::ptr> _sinks;
        std::atomic<LogLevel::value> _limit_level;
        std::mutex _mutex;
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name,
               Formater::ptr formater,
               std::vector<LogSink::ptr> sinks,
               LogLevel::value level)
            : _logger_name(logger_name),
              _formater(formater),
              _sinks(sinks.begin(), sinks.end()),
              _limit_level(level)
        {
        }
        const std::string& name()
        {
            return _logger_name;
        }
        /*完成构造日志消息对象过程并进行格式化,得到格式化消息后的日志消息字符串--然后进行落地输出*/
        virtual void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 构造日志消息对象，然后格式化成一条字符串，最后落地
            // 如果日志等级小于限制等级，则没有必要构造对象
            if (LogLevel::value::DEBUG < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            // 提取的时候不知道是什么格式的数据
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                return;
            }
            serialize(LogLevel::value::DEBUG, file, line, res);
            free(res);
        }
        virtual void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::INFO < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                return;
            }
            serialize(LogLevel::value::INFO, file, line, res);
            free(res);
        }
        virtual void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::WARN < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                return;
            }
            serialize(LogLevel::value::WARN, file, line, res);
            free(res);
        }
        virtual void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::ERROR < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                return;
            }
            serialize(LogLevel::value::ERROR, file, line, res);
            free(res);
        }
        virtual void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::FATAL < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                return;
            }
            serialize(LogLevel::value::FATAL, file, line, res);
            free(res);
        }
    private:
        void serialize(LogLevel::value level, const std::string &file, size_t line, const char* res)
        {
            //构造LogMsg对象
            LogMsg msg(level, file, line, res, _logger_name);
            //将LogMsg对象格式化为一条字符串
            std::string str = _formater->format(msg);
            log(str.c_str(), str.size());
        }
    protected:
        virtual void log(const char *data, size_t len) = 0;
    };
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name,
               Formater::ptr formater,
               std::vector<LogSink::ptr> sinks,
               LogLevel::value level)
            :Logger(logger_name, formater, sinks, level)
        {
        }
    protected:
        void log(const char *data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_sinks.empty())  return;
            for(auto& sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
               Formater::ptr formater,
               std::vector<LogSink::ptr> sinks,
               LogLevel::value level,
               AsyncType looper_type)
            :Logger(logger_name, formater, sinks, level),
            _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type))
        {
        }
        void log(const char *data, size_t len)
        {
            _looper->push(data, len);
        }
        void realLog(Buffer& buffer)
        {
            if(_sinks.empty())  return;
            for(auto& sink : _sinks)
            {
                sink->log(buffer.begin(), buffer.readAbleSize());
            }
        }
        private:
            AsyncLooper::ptr _looper;
    };

    /*使用建造者模式来构造一个日志器对象,而不要让用户直接去构造日志器，简化用户的使用复杂度*/
    //1.抽象一个日志器建造者类(完成日志器对象所需零部件的构建 & 日志器的构建)
    //2.派生出具体的建造者类--局部日志器的建造者 & 全局的日志器建造者(后边添加了全局单例管理器之后，将日志器添加为全局管理)
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    class LoggerBuilder
    {
    public:
        LoggerBuilder():_logger_type(LoggerType::LOGGER_SYNC),_limit_level(LogLevel::value::DEBUG)
        {}
        void buildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }
         void buildEnableUnSafeAsync()
        {
            _looper_type = AsyncType::ASYNC_UNSAFE;
        }
        void buildLoggerName(const std::string& name)
        {
            _logger_name = name;
        }
        void buildformater(const std::string& pattern)
        {
            _formater = std::make_shared<Formater>(pattern);
        }
        template<typename SinkType, typename ...Args>
        void buildSinks(Args ...args)
        {
            LogSink::ptr _psink = std::make_shared<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(_psink);
        }
        void buildLimitLevel(LogLevel::value level)
        {
            _limit_level = level;
        }
        virtual Logger::ptr build() = 0;
    protected:
        LoggerType _logger_type;
        std::string _logger_name;
        Formater::ptr _formater;
        std::vector<LogSink::ptr> _sinks;
        LogLevel::value _limit_level;
        AsyncType _looper_type;
    };

    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false);
            if(_formater.get() == nullptr)
            {
                _formater = std::make_shared<Formater>();
            }
            if(_sinks.empty())
            {
                _sinks.push_back(std::make_shared<StdoutSink>());
            }
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _formater, _sinks, _limit_level, _looper_type);
            }
            return std::make_shared<SyncLogger>(_logger_name, _formater, _sinks, _limit_level);
        }
    };
    
    class LoggerManager
    {
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;//默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
    public:
        static LoggerManager& GetInstance()
        {
            static LoggerManager eton;
            return eton;
        }
        void addLogger(Logger::ptr& logger)
        {
            if(hasLogger(logger->name()))  return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }    
        bool hasLogger(const std::string logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            if(it == _loggers.end())  return false;
            return true;
        }
        Logger::ptr getLogger(const std::string& logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            if(it == _loggers.end())  return Logger::ptr();
            return it->second;
        }
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }
    private:
        LoggerManager()
        {
            std::unique_ptr<mylog::LoggerBuilder> loggerbuild(new mylog::LocalLoggerBuilder());
            loggerbuild->buildLoggerName("root");
            _root_logger = loggerbuild->build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }
        LoggerManager(const LoggerManager& logger_manager) = delete;
        LoggerManager& operator=(const LoggerManager& logger_manager) = delete;

    };

     class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false);
            if(_formater.get() == nullptr)
            {
                _formater = std::make_shared<Formater>();
            }
            if(_sinks.empty())
            {
                _sinks.push_back(std::make_shared<StdoutSink>());
            }
            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _formater, _sinks, _limit_level, _looper_type);
            }
            else 
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _formater, _sinks, _limit_level);
            }
            LoggerManager::GetInstance().addLogger(logger);
            return logger;
        }
    };
    
}
#endif